package com.study.example.gameserver.module.backpack.service;

import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import com.sparrow.common.handler.Handler;
import com.sparrow.common.handler.HandlerManager;
import com.sparrow.common.uuid.TimeUUIDGenerator;
import com.sparrow.event.annotation.EventReceiver;
import com.sparrow.example_message.backpack.resp.RespBackPackAddItem;
import com.sparrow.example_message.backpack.resp.RespBackPackInfo;
import com.sparrow.example_message.backpack.resp.RespBackPackRemoveItem;
import com.sparrow.example_message.backpack.resp.RespBackPackUpdateItem;
import com.sparrow.example_message.backpack.vo.ItemVo;
import com.sparrow.resource.annotation.InjectGlobalValue;
import com.sparrow.resource.cache.ResourceCache;
import com.sparrow.resource.event.ResourceReloadedEvent;
import com.study.example.gameserver.base.def.CDType;
import com.study.example.gameserver.module.backpack.use.ItemUseHandler;
import com.study.example.gameserver.module.backpack.use.handler.AbstractItemUseHandler;
import com.study.example.gameserver.module.backpack.use.handler.ItemUseWithStateHandler;
import com.study.example.gameserver.module.backpack.entity.Item;
import com.study.example.gameserver.module.backpack.entity.PlayerBackPack;
import com.study.example.gameserver.module.backpack.resource.ItemResource;
import com.study.example.gameserver.module.backpack.use.ItemUseContext;
import com.study.example.gameserver.module.player.domain.PlayerDataType;
import com.study.example.gameserver.module.player.entity.Player;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 背包
 */
@Component
public class BackPackService {
    private static Logger logger = LoggerFactory.getLogger(BackPackService.class);

    @Autowired
    private ResourceCache<Integer, ItemResource> itemResourceCache;

    @Autowired
    private TimeUUIDGenerator uuidGenerator;

    @Autowired
    private HandlerManager handlerManager;

    @InjectGlobalValue("back_pack_init_size")
    private int maxGridCount;

    @EventReceiver
    public void onReload(ResourceReloadedEvent event) {
        if (event.getClazz().equals(ItemResource.class)) {
            AbstractItemUseHandler.clearStateHandler();
            logger.info("道具配置热更清理带状态的UseHandler");
        }
    }

    /**
     * 添加道具
     * @param player
     * @param modelId
     * @param num
     */
    public boolean addGoods(Player player, int modelId, int num) {
        if (isBackPackMax(player)) {
            return false;
        }
        ItemResource itemResource = itemResourceCache.getResource(modelId);
        Preconditions.checkNotNull(itemResource, "道具配置id=" + modelId + "不存在");
        //新增的列表
        List<Item> addList = new ArrayList<>();
        //更新的列表
        List<Item> updateList = new ArrayList<>();
        // 自动使用逻辑
        if (itemResource.isAutoUse()) {
            logger.info("玩家{} 获得道具 {} 数量={}，触发自动使用", player.getPlayerId(), modelId, num);
            autoUseItem(player, itemResource, num);
            return true;
        }

        PlayerBackPack playerBackPack = player.getData(PlayerDataType.BackPack);
        ConcurrentHashMap<Long, Item> items = playerBackPack.getItems();
        int stackLimit = itemResource.getStackLimit();
        // 先填充已有未满堆叠
        for (Item item : items.values()) {
            if (item.getModelId() != modelId) {
                continue;
            }

            if (stackLimit > 0 && item.getNum() < stackLimit) {
                int canAdd = Math.min(num, stackLimit - item.getNum());
                item.addNum(canAdd);
                num -= canAdd;
                updateList.add(item);
                if (num == 0) {
                    playerBackPack.update();
                    break;
                }
            } else if (stackLimit <= 0) {
                // 无堆叠限制
                item.addNum(num);
                num = 0;
                updateList.add(item);
                playerBackPack.update();
                break;
            }
        }


        // 剩余数量分堆创建
        while (num > 0) {
            int addNum = stackLimit > 0 ? Math.min(num, stackLimit) : num;
            Item newItem = new Item();
            newItem.setId(uuidGenerator.nextId());
            newItem.setModelId(modelId);
            newItem.setNum(addNum);
            int slotIndex = getEmptySlotIndex(playerBackPack);
            newItem.setIndex(slotIndex);
            playerBackPack.addItem(newItem);
            addList.add(newItem);
            num -= addNum;
        }
        playerBackPack.update();

        if (addList.size() > 0) {
            sendAddItem(player, addList);
        }
        if (updateList.size() > 0) {
            sendUpdateItem(player, updateList);
        }
        return true;
    }

    public boolean isBackPackMax(Player player) {
        PlayerBackPack playerBackPack = player.getData(PlayerDataType.BackPack);
        return playerBackPack.getItems().size() >= maxGridCount;
    }

    public int getEmptySlotIndex(PlayerBackPack playerBackPack) {
        for (int i = 0; i < maxGridCount; i++) {
            if (!hasSlotIndex(playerBackPack, i)) {
                return i;
            }
        }
        throw new RuntimeException("背包找不到空格子 playerId=" + playerBackPack.getPK());
    }

    public boolean hasSlotIndex(PlayerBackPack playerBackPack, int index) {
        for (Map.Entry<Long, Item> e : playerBackPack.getItems().entrySet()) {
            if (e.getValue().getIndex() == index) {
                return true;
            }
        }
        return false;
    }

    private void autoUseItem(Player player, ItemResource itemResource, int num) {
        useGoods(player, itemResource, num);
    }

    public boolean useGoods(Player player, ItemResource itemResource, int num) {
        ItemUseContext itemUseContext = new ItemUseContext();
        itemUseContext.setPlayer(player);
        itemUseContext.setNum(num);
        itemUseContext.setModelId(itemResource.getId());
        itemUseContext.setUseParam(itemResource.getUseParam());
        //先获取无状态handler 没有解析使用参数的
        Handler handler = handlerManager.getHandler(ItemUseHandler.class, itemResource.getUseType());
        if (handler == null) {
            player.sendTip("道具没有使用处理器");
            return false;
        }
        if (!(handler instanceof AbstractItemUseHandler)) {
            return false;
        }

        AbstractItemUseHandler itemUseHandler = (AbstractItemUseHandler) handler;
        //获取已解析参数的带状态的handler
        ItemUseWithStateHandler withStateHandler = itemUseHandler.getHandler(itemResource.getId(), itemUseContext.getUseParam());
        return withStateHandler.handle(itemUseContext);
    }


    /**
     * 扣除道具
     * @param player 玩家对象
     * @param modelId 道具ID
     * @param num 扣除数量
     * @return 扣除成功返回 true，数量不足返回 false
     */
    public boolean deductGoods(Player player, int modelId, int num) {
        if (num <= 0) {
            return false;
        }
        ItemResource itemResource = itemResourceCache.getResource(modelId);
        Preconditions.checkNotNull(itemResource, "道具配置id=" + modelId + "不存在");
        PlayerBackPack playerBackPack = player.getData(PlayerDataType.BackPack);
        ConcurrentHashMap<Long, Item> items = playerBackPack.getItems();
        // 收集符合条件的item并计算总数
        List<Item> candidates = new ArrayList<>();

        List<Item> updateList = new ArrayList<>();
        List<Item> deleteList = new ArrayList<>();
        int total = 0;
        for (Item item : items.values()) {
            if (item.getModelId() == modelId) {
                candidates.add(item);
                total += item.getNum();
                if (total >= num) {
                    break;
                }
            }
        }
        if (total < num) {
            return false;
        }
        // 开始扣除
        int remain = num;
        for (Item item : candidates) {
            int curr = item.getNum();
            if (curr <= remain) {
                remain -= curr;
                items.remove(item.getId());
                deleteList.add(item);
            } else {
                item.setNum(curr - remain);
                remain = 0;
                updateList.add(item);
                break;
            }
        }
        playerBackPack.update();

        if (updateList.size() > 0) {
            sendUpdateItem(player, updateList);
        }
        if (deleteList.size() > 0) {
            sendRemoveItem(player, deleteList);
        }
        return true;
    }

    public void consumeGoods(Player player, long goodsId, int num) {
        PlayerBackPack playerBackPack = player.getData(PlayerDataType.BackPack);
        Item item = playerBackPack.getItems().get(goodsId);
        Preconditions.checkState(item.getNum() >= num);
        int remain = item.getNum() - num;
        if (remain > 0) {
            item.setNum(remain);
            sendUpdateItem(player, Lists.newArrayList(item));
        } else {
            playerBackPack.getItems().remove(goodsId);
            sendRemoveItem(player, Lists.newArrayList(item));
        }
        playerBackPack.update();
    }

    public boolean removeGoods(Player player, long id) {
        PlayerBackPack playerBackPack = player.getData(PlayerDataType.BackPack);
        return playerBackPack.removeItem(id);
    }

    /**
     * 整理背包
     * @param player
     */
    public void organize(Player player) {
        PlayerBackPack playerBackPack = player.getData(PlayerDataType.BackPack);
        List<Item> items = playerBackPack.getItems().values().stream().collect(Collectors.toList());

        Map<Integer, List<Item>> stackableMap = new HashMap<>();
        List<Item> unstackableItems = new ArrayList<>();
        for (Item item : items) {
            int modelId = item.getModelId();
            ItemResource resource = itemResourceCache.getResource(modelId);
            int stackLimit = resource.getStackLimit();
            if (stackLimit > 0) {
                stackableMap.computeIfAbsent(modelId, k -> new ArrayList<>()).add(item);
            } else {
                unstackableItems.add(item);
            }
        }

        List<Item> sortedItems = new ArrayList<>();

        // 合并可堆叠道具
        for (Map.Entry<Integer, List<Item>> entry : stackableMap.entrySet()) {
            int modelId = entry.getKey();
            List<Item> sameItems = entry.getValue();
            ItemResource resource = itemResourceCache.getResource(modelId);
            int stackLimit = resource.getStackLimit();

            int total = 0;
            for (Item item : sameItems) {
                total += item.getNum();
            }

            while (total > 0) {
                int addNum = Math.min(total, stackLimit);
                Item newItem = new Item();
                newItem.setId(uuidGenerator.nextId());
                newItem.setModelId(modelId);
                newItem.setNum(addNum);
                sortedItems.add(newItem);
                total -= addNum;
            }
        }

        // 加入不可堆叠道具（保持原数量）
        sortedItems.addAll(unstackableItems);

        // 最终统一排序：按 modelId 升序
        sortedItems.sort(Comparator.comparingInt(Item::getModelId));

        int index = 0;
        for (Item item : sortedItems) {
            item.setIndex(index++);
        }
        // 清空原始 items
        playerBackPack.getItems().clear();
        // 加入背包
        for (Item item : sortedItems) {
            playerBackPack.getItems().put(item.getId(), item);
        }

        playerBackPack.setSortedItems(sortedItems);
        playerBackPack.update();
    }

    public void sendBackPackInfo(Player player) {
        PlayerBackPack playerBackPack = player.getData(PlayerDataType.BackPack);
        List<Item> items = playerBackPack.getSortedItems();
        List<ItemVo> itemVos = items.stream().map(e -> e.toItemVo()).collect(Collectors.toList());
        RespBackPackInfo resp = new RespBackPackInfo();
        resp.setItemVos(itemVos);
        long endTime = player.getCdBox().getEndTime(CDType.BackPackSortCD);
        if (endTime > System.currentTimeMillis()) {
            resp.setSortCDEndTime((int) TimeUnit.MILLISECONDS.toSeconds(endTime));
        }
        player.sendPacket(resp);
    }

    public void sendAddItem(Player player, List<Item> list) {
        RespBackPackAddItem packet = new RespBackPackAddItem();
        List<ItemVo> itemVos = list.stream().map(e -> e.toItemVo()).collect(Collectors.toList());
        packet.setItemVos(itemVos);
        player.sendPacket(packet);
    }

    public void sendUpdateItem(Player player, List<Item> list) {
        RespBackPackUpdateItem packet = new RespBackPackUpdateItem();
        List<ItemVo> itemVos = list.stream().map(e -> e.toItemVo()).collect(Collectors.toList());
        packet.setItemVos(itemVos);
        player.sendPacket(packet);
    }

    public void sendRemoveItem(Player player, List<Item> list) {
        List<Long> removes = list.stream().mapToLong(e -> e.getId()).boxed().collect(Collectors.toList());
        RespBackPackRemoveItem packet = new RespBackPackRemoveItem();
        packet.setList(removes);
        player.sendPacket(packet);
    }

}

